我們在 [Day4] Lex - State 中,有介紹 State 的基本用法。我們可以經由不同 State 之間的切換,做不同字串的分類與標記方式。然而,如果某個標記方式在多個 State 都通用的話呢?暴力解當然就是把這個規則在每個 State 都寫一遍,但其實有更方便的做法。
我們可以將適用於多個State的規則寫成像下面這樣:
<STATE1, STATE2>reg_expr { operations(); }
如此一來,reg_expr 這個規則就能同時在 STATE1 跟 STATE2 使用了。
如果要讓某個規則在所有 State 都適用的話,可以寫成這樣:
<*>reg_expr { operations(); }
給定一份文件,內容有一段DNA核酸序列(由A, T, C, G組成),以及一段RNA序列(由A, U, C, G組成)。
試著計算各個字母(含氮鹼基)的數量。
檔案的格式如下:
* DNA
AAAAACCCCCAAAAACCCCCCAAAAAGGGTTT
* RNA
AAAAAAAAAAUUUUUCCCCCGGGGGAAAAA
%{
int numA, numT, numC, numG, numU;
%}
%x DNA_S
%x RNA_S
blank [ \t]
DNA
字樣時,要進入 DNA_S
RNA
字樣時,要進入 RNA_S
*
字樣時,要回到初始 State → 這部分常常會被忘記!%%
\* { BEGIN 0; }
<*>{blank} { ; }
<*>\n { ; }
DNA { BEGIN DNA_S; }
RNA { BEGIN RNA_S; }
<DNA_S,RNA_S>A { numA++; }
<DNA_S,RNA_S>C { numC++; }
<DNA_S,RNA_S>G { numG++; }
<DNA_S>T { numT++; }
<RNA_S>U { numU++; }
<DNA_S,RNA_S>\* { BEGIN 0; }
<DNA_S>[^ACGT] { printf("wrong alphabet in DNA: "); ECHO; printf(".\n"); }
<RNA_S>[^ACGU] { printf("wrong alphabet in RNA: "); ECHO; printf(".\n"); }
. { ; }
%{
int numA, numT, numC, numG, numU;
%}
%x DNA_S
%x RNA_S
blank [ \t]
%%
\* { BEGIN 0; }
<*>{blank} { ; }
<*>\n { ; }
DNA { BEGIN DNA_S; }
RNA { BEGIN RNA_S; }
<DNA_S,RNA_S>A { numA++; }
<DNA_S,RNA_S>C { numC++; }
<DNA_S,RNA_S>G { numG++; }
<DNA_S>T { numT++; }
<RNA_S>U { numU++; }
<DNA_S,RNA_S>\* { BEGIN 0; }
<DNA_S>[^ACGT] { printf("wrong alphabet in DNA: "); ECHO; printf(".\n"); }
<RNA_S>[^ACGU] { printf("wrong alphabet in RNA: "); ECHO; printf(".\n"); }
. { ; }
%%
int yywrap(void) {
return 1;
}
int main(void) {
const char* sFile = "file.txt";
FILE* fp = fopen(sFile, "r");
if (fp == NULL)
{
printf("cannot open %s\n", sFile);
return -1;
}
yyin = fp;
yylex();
printf("Counter :\nA: %d\nT: %d\nC: %d\nG: %d\nU: %d\n", numA, numT, numC, numG, numU);
return 0;
}
* DNA
AAAAACCCCCAAAAACCCCCCAAAAAGGGTTT
* RNA
AAAAAAAAAAUUUUUCCCCCGGGGGAAAAA
Counter :
A: 30
T: 3
C: 16
G: 8
U: 5
繼第一次介紹 State 後,今天的 state 語法可以簡化 lex 規則,容易維護。